
#pragma once

#include "Stream.h"

#define DV_CHECKTYPE ASSERT

#define countof(x) (sizeof(x)/sizeof(x[0]))

inline tstring format(const TCHAR *fmt,...)
{
	va_list ap;
	va_start(ap,fmt);
	TCHAR buf[1024];
	StringCbVPrintf(buf, countof(buf), fmt, ap);
	return tstring(buf);
}




class DetourVariant
{
public:
	const static int LONG = VT_I8;
	const static int INT = VT_I4;
	const static int WSTR = VT_BSTR;
	const static int STR = VT_BSTR;
	const static int PTR = VT_PTR;

public:
	struct{
		int dynvalue:1;
		int dyntree:1;
		int type:14;
	};

	union {
		long lVal;
		int intVal;
		struct{
			void *ptr;
			int len;
		} ptr;
	};

public:
	DetourVariant *next;
	DetourVariant *child;
	DetourVariant *name;

	DetourVariant()
	{
		Zero();
	}

	inline void Zero()
	{
		lVal = 0;
		type = 0;
		dynvalue = 0;
		dyntree = 0;
		next=child=name = NULL;
	}

	
	inline static void Release(DetourVariant *&p)
	{
		if(NULL!=p)
			delete p;
		p = NULL;
	}

	inline static void ReleaseNexts( DetourVariant *p)
	{
		DetourVariant *pp = p->next;
		p->next=NULL;
		while(NULL!=pp)
		{
			DetourVariant *dead = pp;
			pp=pp->next;
			delete dead;
		}
	}

	inline static DetourVariant *New()
	{
		return new DetourVariant();
	}

	virtual ~DetourVariant()
	{
		ReleaseValue();
		if(dyntree==1)
			ReleaseNexts(this);
	}
	

	void ReleaseValue()
	{
		if(dynvalue)
		{
			if(type==PTR || type==STR || type==WSTR)
				if(this->ptr.ptr!=NULL)
					delete this->ptr.ptr;
		}
		Zero();
	}


	DetourVariant(long lVal)
	{
		Zero();
		*this=lVal;
	}

	DetourVariant(int intVal)
	{
		Zero();
		*this = intVal;
	}

	DetourVariant(const TCHAR *pch)
	{
		Zero();
		*this = pch;
	}

	

	DetourVariant(void *ptr, int len)
	{
		Zero();
		this->type=PTR;
		this->ptr.ptr = ptr;
		this->ptr.len = len;
	}

	DetourVariant &operator=(long lVal)
	{
		this->type=LONG;
		this->lVal = lVal;
		return *this;
	}

	DetourVariant& operator=(int intVal)
	{
		this->type=INT;
		this->intVal = intVal;
		return *this;
	}

	DetourVariant& operator=(const DetourVariant &rhs)
	{
		type=rhs.type;
		lVal=rhs.lVal;
		return *this;
	}

	DetourVariant& operator=(const TCHAR *pch)
	{
		this->type=STR;
		this->ptr.ptr = (void*) pch;
		size_t len;
		StringCbLength(pch, STRSAFE_MAX_CCH * sizeof(TCHAR), &len);
		this->ptr.len = len;
		return *this;
	}

	DetourVariant& from(const TCHAR *pch)
	{
		this->dynvalue=1;
		return *this=_tcsdup(pch);
	}

	DetourVariant& from(const void*p, int len)
	{
		this->dynvalue=1;
		void *pc = new byte[len];
		memcpy(pc,p,len);
		this->ptr.ptr=pc;
		this->ptr.len=len;
		return *this;
	}

	operator const char*() const
	{
		DV_CHECKTYPE(type==STR);
		return (const char*)ptr.ptr;
	}

	tstring strValue() const
	{
		switch(type)
		{
		case STR: return tstring((const TCHAR*)ptr.ptr);
		case INT: return format(_T("%d"), intVal);
		case LONG: return format(_T("%ld"), lVal);
		case PTR: return format(_T("[%d]"), ptr.len);
		}
		ASSERT(0);
		return tstring(_T(""));
	}

	std::list<tstring> strList() const
	{
		std::list<tstring> sl;

		for(const DetourVariant *pv = this; pv; pv=pv->next)
		{
			sl.push_back(pv->strValue());
		}
		return sl;
	}


	operator int() const
	{
		DV_CHECKTYPE(type==INT);
		return intVal;
	}

	operator long() const
	{
		DV_CHECKTYPE(type==LONG);
		return lVal;
	}

	operator const void*() const
	{
		DV_CHECKTYPE(type==PTR);
		return ptr.ptr;
	}

	int length() const
	{
		DV_CHECKTYPE(type==STR || type==WSTR || type==PTR);
		return ptr.len;
	}

	inline DetourVariant &operator+=(DetourVariant* b)
	{
		next = b;
		return *this;
	}

	inline DetourVariant &operator+=(DetourVariant &b)
	{
		next = &b;
		return *this;
	}

	inline DetourVariant &operator+=(LPCTSTR b)
	{
		next = new DetourVariant(b);
		return *this;
	}

	operator DetourVariant*()
	{
		return this;
	}

};

template<typename T> inline tstring toString(const T &b)
{
	return "";
}

template<> inline tstring toString<PTCHAR>(const PTCHAR& s)
{
	return tstring((LPCTSTR)s);
}

template<> inline tstring toString<tstring>(const tstring& s)
{
	return s;
}

template<> inline tstring toString<long>(const long &l)
{
	return format(_T("%ld"),l);
}

template<> inline tstring toString<DetourVariant>(const DetourVariant & b)
{
	return b.strValue();
}

template<typename T> inline tstring format(T coll)
{
	tstring sRes;
	for(T::const_iterator it = coll.begin(); it!=coll.end(); it++)
	{
		if(it!=coll.begin())
			sRes.append(",");
		sRes.append(toString(*it));
	}
	return sRes;
}

class Stream;

class DetourFunction
{
public:
	inline DetourFunction(LPCTSTR name)
	{
		m_pArgs = DetourVariant::New();
		m_pMeta = DetourVariant::New();
		dynmeta=1;
		dynargs=1;
		m_pArgs->name = m_pMeta;
		m_pArgs->from(name);
	}

	DetourFunction()
	{
		m_pArgs = DetourVariant::New();
		m_pMeta = DetourVariant::New();
		dynmeta=1;
		dynargs=1;
		m_pArgs->name = m_pMeta;
		*m_pArgs=_T("");
	}

	~DetourFunction()
	{
		if(dynmeta)
			DetourVariant::Release(m_pMeta);
		if(dynargs)
			DetourVariant::Release(m_pArgs);
	}

	std::list<tstring> ArgValues() 
	{
		if(NULL!=m_pArgs->next);
			return m_pArgs->next->strList();
		return std::list<tstring>();
	}

	std::list<tstring> ArgNames() 
	{
		if(NULL!=m_pMeta->next);
			return m_pMeta->next->strList();
		return std::list<tstring>();
	}

	void Meta(LPCTSTR pszFirst,...)
	{
		ASSERT(NULL==m_pMeta->next);
		m_pMeta->next = new DetourVariant(pszFirst);

	}

	DetourVariant &Args()
	{
		return *m_pArgs;
	}

	inline tstring Name()
	{
		return m_pMeta->strValue();
	}

	virtual void Write(ValueStream *pStream) const
	{
		pStream->WriteAll(m_pArgs);
	}

	virtual void Read(ValueStream *pStream)
	{
		DetourVariant::Release(m_pArgs);
		m_pArgs = pStream->ReadAll();
	}

public:
	DetourVariant *m_pArgs;
	DetourVariant *m_pMeta;
	struct{
		int dynmeta:1;
		int dynargs:1;
	};
};


